import { ShareDefine } from "../../../Game/Scripts/Config/ShareDefine";
import ControlManager from "../../../Game/Scripts/dataMgr/ControlManager";
import LoginMgr from "../../../Game/Scripts/dataMgr/LoginMgr";
import ClientManager from "../Managers/ClientManager";
import EventMgr from "../Managers/EventMgr";


const { ccclass, property } = cc._decorator;

class Request {
	id: null;
	event: null;
	param: null;
	callback: null;
	errcb: null;
	Init() {
		this.id = null;
		this.event = null;
		this.param = null;
		this.callback = null; //(msg: any) => void;
		this.errcb = null; //(head: any, code: number, msg: string) => void;
	}
}

var MessageType = {
	None: 0, // 空白定义
	/**
	 * 对request的反馈，无需回包
	 */
	Response: 1,
	/**
	 * 向其他服务器请求，必定回包
	 */
	Request: 2,
	/**
	 * 向其他服务器发送监控信息，无需回包
	 */
	Notify: 3,
}


@ccclass
export default class NetWork extends cc.Component {
	public static Instance: NetWork = null;
	handshakeCallback: any;
	eventCallback: any;
	isTestWss: any;
	socket: any;
	isConnectIng: any;
	host: string;
	port: number;
	url: string;
	isReconnecting: any;
	reConnectTimeID: any;
	tryReConnectCount: number;
	bDisconnectByLogout: boolean;
	connectAccountID: any;
	requests: any;
	losts: any[];
	cacheRequestList: any[];
	requestid: any;
	BigPack: any;
	onLoad() {
		if (NetWork.Instance !== null) {
			this.destroy();
			return;
		}
		NetWork.Instance = this;
	}

	//初始化连接
	InitWebSocket(host: string, port: number, eventCallback, handshakeCallback) {
		console.log("============初始化连接socket===========");
		console.log("InitWebSocket(%s:%s)", host, port, "w-g");

		this.handshakeCallback = handshakeCallback;
		this.eventCallback = eventCallback;
		if (this.isTestWss) {
			port = 9988;
		}
		let readyState = 0;
		if (this.Connected() && this.socket) {
			readyState = this.socket.readyState;
		}

		//已经连接成功,直接回调握手函数
		if (this.Connected() && this.socket && (readyState == 1) && this.host == host && this.port == port) {
			console.log("连接未断开");
			this.handshakeCallback(false);
		}
		else {
			this.host = host;
			this.port = port;
			if (this.isTestWss) {
				this.url = ['wss://', host, ':', port].join("");
			} else {
				this.url = ['ws://', host, ':', port].join("");
			}
			// this.url = ['ws://', host, ':', port].join("");
			this.Connect();
		}
	}

	// 连接Java服务器
	Connect() {
		if (ControlManager.Instance.CheckVpnUsed(this.Connect.bind(this))) {
			return;
		}
		if (ClientManager.Instance.isInHall() == false) {
			console.log("不在大厅内，Connect");
			return;
		}

		if (this.socket) {
			console.log("socket连接已存在，需先关闭");
			if (this.socket.close) {
				this.socket.close();
			}
			else if (this.socket.disconnect) {
				this.socket.disconnect();
			}
			this.socket = null;
		}
		let url = this.url;

		this.socket = new WebSocket(this.url);
		console.log("连接Java服务器:" + this.url);
		console.log("开始连接:%s 是否重连(%s)", this.url, this.isReconnecting, "w-g");
		this.socket.binaryType = 'arraybuffer';
		this.socket.onopen = this.OnOpen.bind(this);
		this.socket.onmessage = this.OnMessage.bind(this);
		this.socket.onerror = this.OnError.bind(this);
		this.socket.onclose = this.OnClose.bind(this); // 服务器断开
		let that = this;
		setTimeout(() => {
			if (this.isConnectIng == true) {
				console.log("setTimeout 已经检测到连接成功");
			} else {
				this.OnClose({ "type": 'timeout', "target": { "URL": url } });
			}
		}, 2000);
	}


	//是否连接中
	Connected() {
		return this.isConnectIng;
	}

	//----------回掉函数------------------
	//连接开启
	OnOpen(event) {
		if (this.isTestWss) {
			console.error("WebSocket 连接开启 OnOpen: ", event);
		}
		//如果是重连成功回调
		if (this.isReconnecting) {
			console.log("重连成功 OnOpen(%s)", this.url, "w-g");
		}
		else {
			console.log("连接成功 OnOpen(%s)", this.url, "w-g");
		}

		if (this.reConnectTimeID) {
			clearTimeout(this.reConnectTimeID);
			this.reConnectTimeID = 0;
		}

		this.isConnectIng = true;
		this.tryReConnectCount = 0;
		//连接成功回调
		if (this.handshakeCallback) {
			//回调确认是重连在置为false
			this.handshakeCallback(this.isReconnecting);
		}
		this.isReconnecting = false;
	}

	//发起重连
	ReConnect() {
		if (ControlManager.Instance.IsOpenVpn()) {
			return;
		}

		if (ClientManager.Instance.isInHall() == false) {
			console.log("不在大厅内，ReConnect");
			return;
		}
		this.Disconnect();

		//玩家登录服务器后,才需要建立重连,否则不重连,登录过程当断网处理,否则登录请求一直重连
		if (this.connectAccountID) {
			if (this.isReconnecting) {
				console.log("已经在重连中,不需要尝试重连");
				return true
			}
			console.log("重连中...");
			this.isReconnecting = true;
			this.Connect();
			app.Client.OnEvent("ModalLayer", "StartReConnect");
			return true
		}
		else {
			console.log("没有玩家建立连接,不需要重连");
			this.isReconnecting = false;
			return false
		}

	}

	//断开连接
	Disconnect(byLogout = false) {
		if (this.isTestWss) {
			console.error("WebSocket 断开连接:  ", byLogout);
		}
		if (this.isConnectIng && this.socket) {
			if (this.socket.close) {
				this.socket.close();
			}
			else if (this.socket.disconnect) {
				this.socket.disconnect();
			}
			console.log("Disconnect(%s)", this.url);
			this.socket = null;
			this.isConnectIng = false;
			if (byLogout)
				this.bDisconnectByLogout = true;
		}
	}

	//接受到数据
	OnMessage(event) {
		let byte = "";
		let messageType = 0;
		let srcType = 0;
		let srcId = 0;
		let descType = 0;
		let descId = 0;
		let eventName = "";
		let sequence = 0;
		let errorCode = 0;
		let strBody = "";
		let body = {};
		let allPack = 0;
		let nowPack = 0;
		if (ClientManager.Instance.isInHall() == false) {
			console.log("不在大厅内，OnMessage");
			return;
		}
		try {
			byte = new EgretReadByteArray(event.data);

			messageType = byte.readByte();
			srcType = byte.readByte();
			srcId = (byte.readInt() << 32) + byte.readInt();
			descType = byte.readByte();
			descId = (byte.readInt() << 32) + byte.readInt();

			allPack = byte.readShort();
			nowPack = byte.readShort();
			eventName = byte.readUTF().toLocaleLowerCase();

			sequence = byte.readShort();
			errorCode = byte.readShort();
			strBody = byte.readUTF();

			if (allPack > 1) {
				this.BigPack[nowPack] = strBody;
				if (this.BigPack.length == allPack) {
					let BigstrBody = '';
					for (let i = 0; i < allPack; i++) {
						BigstrBody = BigstrBody + this.BigPack[i];
					};
					strBody = BigstrBody;
					this.BigPack = [];
				} else {
					console.log("NetWork allPack error length:%s", allPack);
					return;
				}
			}

			if (errorCode) {
				body = { "Msg": strBody };
			}
			else {
				body = JSON.parse(strBody);
			}

			console.log("OnMessage(%s,%s,%s,%s,%s)", messageType, srcType, srcId, descType, descId);
		}
		catch (error) {
			console.log("OnMessage error:%s", error.stack);
			return
		}

		if (this.IsDevelopment()) {
			console.log("[Recv](%s,%s,%s):", sequence, eventName, errorCode, body, "b-gb");
		}

		if (errorCode) {
			this.eventCallback("OnCodeError", [eventName, errorCode, body]);
		}
		else {
			//更新心跳包
			ClientManager.Instance.UpdateHearTime();
			this.eventCallback("OnReceive", [eventName, body]);
		}

		var req = this.requests[sequence];
		//请求事件可能注册了回调函数
		if (!req) {
			return
		}
		//单次回调事件,删除记录
		delete this.requests[sequence];

		try {
			if (errorCode) {
				var errorFunc = req.errcb;
				if (errorFunc) {
					errorFunc(body);
				}
			}
			else if (req.callback) {
				req.callback(body);
			}
		}
		catch (error) {
			console.log("error:%s", error.stack, body);
		}
	}

	UpdateAccessPoint() {
		LoginMgr.Instance.UpdateAccessPoint();
		let clientConfig = ClientManager.Instance.GetClientConfig();
		let gameServerIP = clientConfig["GameServerIP"];
		let gameServerPort = clientConfig["GameServerPort"];
		//换节点
		this.url = ['ws://', gameServerIP, ':', gameServerPort].join("");
	}

	//连接错误
	OnError(event) {
		if (ClientManager.Instance.isInHall() == false) {
			console.log("不在大厅内，OnError");
			return;
		}
		if (this.isTestWss) {
			console.error("WebSocket 连接错误 OnError: ", event);
		}
		if (this.url != event.target.URL) {
			return;
		}
		//如果重连中,网络连接失败
		if (this.isReconnecting) {
			console.log("大厅重连失败");
			//如果有启动重连定时时间,清除掉回调
			if (this.reConnectTimeID) {
				clearTimeout(this.reConnectTimeID);
			}
			this.tryReConnectCount++;
			let that = this;
			this.reConnectTimeID = setTimeout(function () {
				that.UpdateAccessPoint();
				that.isReconnecting = false;
				that.ReConnect();
			}, 2000);
		}
		else {
			//连接错误
			this.eventCallback("OnError");
		}
	}

	//关闭请求(服务器关闭请求)
	OnClose(event) {
		if (ClientManager.Instance.isInHall() == false) {
			console.log("不在大厅内，OnClose");
			return;
		}
		if (this.isTestWss) {
			console.error("WebSocket 关闭请求(服务器关闭请求) OnClose: ", event);
		}
		if (this.url != event.target.URL) {
			return;
		}
		console.log("OnClose:%s,%s", this.url, event.type);
		if (this.isReconnecting) {
			console.log("OnClose isReconnecting");
		}
		else {
			this.eventCallback("OnClose", [this.bDisconnectByLogout]);
			//初始化回调
			// this.handshakeCallback = null;

			// this.eventCallback = null;
			this.Disconnect();
		}
		this.isConnectIng = false;

		if (this.bDisconnectByLogout) {//自己断开的不自动重连
			this.bDisconnectByLogout = false;
			this.isReconnecting = false;
			return
		}

		if (0 == this.tryReConnectCount) {
			console.log('NetWork OnClose tryReConnect');
			this.tryReConnectCount++;
			if (this.reConnectTimeID) {
				clearTimeout(this.reConnectTimeID);
				this.reConnectTimeID = 0;
			}
			let that = this;
			this.reConnectTimeID = setTimeout(function () {
				that.UpdateAccessPoint();
				that.isReconnecting = false;
				that.ReConnect();
			}, 1000);
		}
	}

	//创建一个新的封包事件结构体
	GetNewRequest() {

		let req = this.cacheRequestList.pop();
		if (!req) {
			req = new Request();
		}
		req.id = this.requestid++;

		//修改请求头ID,避免超过字段值
		if (this.requestid >= 60000) {
			this.requestid = 1;
		}
		return req
	}

	// 发送封包请求 可以携带callback，errorCallback回调
	Request(eventName, sendPack, callback = null, errorCallback = null) {
		if (ControlManager.Instance.IsOpenVpn()) {
			return;
		}
		if (ClientManager.Instance.isInHall() == false) {
			console.log("不在大厅内，Request");
			return;
		}
		eventName = eventName.toLocaleLowerCase();

		// 发送请求
		let req = this.GetNewRequest();
		req.event = eventName;
		req.param = sendPack;
		req.callback = callback;
		req.errcb = errorCallback;

		this.requests[req.id] = req;

		var head = {
			messageType: MessageType.Request,
			event: eventName,
			sequence: req.id
		};

		if (this.Connected()) {
			//如果还在重连建立中,积压封包
			if (this.isReconnecting) {
				console.log("重连中,不能发送封包");
				this.AddLostPack(head, sendPack);
			}
			else {
				if (this.IsDevelopment()) {
					console.warn("[Send](%s) : ", eventName, sendPack, "b-g");
				}
				console.log("开始写入并发送封包");
				this.write(head, sendPack);
			}
		}
		else {
			console.log("未建立连接,不能发送封包");
			//如果成功开启重连,则积压封包
			if (this.ReConnect()) {
				this.AddLostPack(head, sendPack);
			}
			else {
				console.error("未建立连接,不能发送封包(%s):", eventName, sendPack);
				//关闭模态层
				EventMgr.Instance.Emit("ModalLayer", "ReceiveNet");
			}
		}
	}

	//通知服务器
	Notify(eventName, sendPack) {
		if (ClientManager.Instance.isInHall() == false) {
			console.log("不在大厅内，Notify");
			return;
		}
		eventName = eventName.toLocaleLowerCase();
		var head = {
			messageType: MessageType.Notify,
			event: eventName,
			sequence: 0
		};

		if (this.Connected()) {
			//如果还在重连建立中,积压封包
			if (this.isReconnecting) {
				this.AddLostPack(head, sendPack);
			}
			else {
				if (this.IsDevelopment()) {
					console.log("[Notify](%s) : ", eventName, sendPack, "b-g");
				}
				this.write(head, sendPack);
			}
		}
		else {
			//如果成功开启重连,则积压封包
			if (this.ReConnect()) {
				this.AddLostPack(head, sendPack);
			}
			else {
				console.warn("未建立连接,不能发送封包(%s):", eventName, sendPack);
				//关闭模态层
				EventMgr.Instance.Emit("ModalLayer", "ReceiveNet");
			}
		}
	}


	write(head: { messageType: number; event: any; sequence: any; }, sendPack: any) {
		throw new Error("Method not implemented.");
	}
	//追加丢失的封包
	AddLostPack(head, sendPack) {
		let eventName = head.event;

		let lostCount = this.losts.length;
		for (let index = 0; index < lostCount; index++) {
			var req = this.losts[index];
			//存在相同的封包,过滤???
			if (req["head"]["event"] == eventName) {
				console.error("AddLostPack have find(%s):", eventName, req["param"], sendPack);
				return
			}
		}
		this.losts.push({ head: head, param: sendPack });
		console.log("重连建立中,积压封包(%s):", eventName, sendPack);
	}

	//是否开发者模式
	IsDevelopment() {
		return ShareDefine.IsDevelopment
	}
}
